///*******************************************************************************************************
// * @brief    通用协议解析器(依赖ringbuffer模块)
// * Change Logs:
// * Date           Author         Email                   Notes
// * 2023-09-08      子恒     3393018959@qq.com      Initial version
// * 2023-09-18      子恒     3393018959@qq.com     修改接口设计，支持多协议，优化调试日志
// * 2023-10-23	     子恒     3393018959@qq.com     增加接口函数b_frame_fifo_get，                                               用于从fifo中阻塞式获取预期长度数据
// * 2024-07-04	     子恒     3393018959@qq.com     优化帧头帧尾长度为0情况下的支持
// * 2024-07-05	     子恒     3393018959@qq.com     优化uart_get_frame_len_cb返回值为0时的处理策略
// * 2024-07-09	     子恒     3393018959@qq.com     帧头为0时,校验不通过应至少丢弃1个数据
// * 2024-07-09	     子恒     3393018959@qq.com     输入/输出缓存大小分开配置，增加调试信息等级配置
// * 2024-12-20	     子恒     3393018959@qq.com     修复对多协议的支持的相关bug
// * 2025-09-04	     子恒     3393018959@qq.com     输入输出缓冲区大小不通过宏定义配置，在初始化时进行配置，方便多协议解析器时合理利用内存
// *******************************************************************************************************/

#ifndef __B_PROTOCOL_CORE_H__
#define __B_PROTOCOL_CORE_H__

#include "stdio.h"
#include "string.h"
#include "stdint.h"
#include "ringbuffer.h"

#ifdef _B_PROTOCOL_CORE_GLOBALS_
#define PROTOCOL_CORE_EXTERN
#else
#define PROTOCOL_CORE_EXTERN extern
#endif

#define B_SUCCESS 0
#define B_ERROR 1

// 使能调试信息输出
#define EN_FRAME_DEBUG 0

// 空闲钩子函数的计数周期
#define IDIE_TIMER_US 1000

// 波特率
#define FRAME_BAUD_RATE 9600

// 空闲因子，用于消除帧头/数据长度解算错误
#define IDIE_FACTOR 4

#define FRAME_LOG(p, lvl, ...)                         \
    do                                                 \
    {                                                  \
        if ((p) && (p)->frame_init.log_level >= (lvl)) \
        {                                              \
            xprintf(__VA_ARGS__);                      \
        }                                              \
    } while (0)

#define FRAME_RAW_INFO_PRINTF(p, ...) FRAME_LOG(p, 1, __VA_ARGS__)
#define FRAME_LOG_INFO_PRINTF(p, ...) FRAME_LOG(p, 2, __VA_ARGS__)

#if (EN_FRAME_DEBUG == 0) /* 全局强制关闭 */
#undef FRAME_LOG
#define FRAME_LOG(p, lvl, ...) ((void)0)
#undef FRAME_RAW_INFO_PRINTF
#define FRAME_RAW_INFO_PRINTF(p, ...) ((void)0)
#undef FRAME_LOG_INFO_PRINTF
#define FRAME_LOG_INFO_PRINTF(p, ...) ((void)0)
#endif

// 校验函数原型
// buffer，指向一帧数据
// len，该帧数据长度
typedef uint8_t b_check_cb_t(uint8_t *buffer, uint16_t len);

// 获取帧长函数原型
// buffer，指向一帧数据
// len，该帧数据长度
typedef uint16_t b_get_frame_len_cb_t(uint8_t *buffer, uint16_t len);

// 协议描述
typedef struct
{
    const uint8_t *pname;
    const char *head;  /* 指向帧头数据 */
    const char *end;   /* 指向帧尾数据 */
    uint16_t head_len; /* 帧头长度 */
    uint16_t end_len;  /* 帧尾长度 */
    b_get_frame_len_cb_t *get_frame_len_cb;
    b_check_cb_t *check_cb;    /* 帧校验函数指针 */
    uint8_t *in_frame_buffer;  /*输入原始数据缓冲队列*/
    uint8_t *out_frame_buffer; /*解析数据缓冲区*/
    uint16_t in_buffer_len;    /*输入原始数据缓冲区大小，2的N次幂有效*/
    uint16_t out_buffer_len;   /*输出数据缓冲区大小*/
    uint8_t log_level;
} b_frame_init_type;

// 协议类
typedef struct
{
    b_frame_init_type frame_init;
    // 协议处理需要用到的中间变量，用户不可操作
    ring_buf_t _frame_ring;
    uint32_t _idie_timer;
    uint32_t _systick;
    uint8_t head_match_flg;
} b_frame_type;

// 协议格式描述初始化函数
// pframe:传入协议类指针
// pframeinit:协议描述结构体
// ret: B_SUCESS/B_ERR
// note: pframe->in_frame_buffer和pframe->out_frame_buffer的大小必须是2的n次幂 ！！！！
extern uint8_t b_frame_init(b_frame_type *pframe, b_frame_init_type *pframeinit);

// 以IDIE_TIMER_US周期对该函数进行调用
// pframe:传入协议类指针
// 建议调用周期1ms
extern void b_frame_idie_timer(b_frame_type *pframe);

// 将数据丢入协议解析器缓冲区
// pframe:传入协议类指针
// dat：指向数据buffer
// len：丢入的数据长度
// ret：B_SUCESS，数据成功放到缓冲区，B_ERR，数据放入缓冲区失败
extern uint8_t b_frame_put(b_frame_type *pframe, uint8_t *dat, uint32_t len);

// 清空协议类指针中收到的数据
// pframe:传入协议类指针
extern void b_frame_fifo_clear(b_frame_type *pframe);

// 将数据从协议解析器缓冲区中取出
// pframe:传入协议类指针
// dat：指向数据buffer
// len：预期取出的数据长度
// timerout:超时时间
// ret：实际取出的数据长度
extern uint32_t b_frame_fifo_get(b_frame_type *pframe, uint8_t *dat, uint32_t len, uint32_t timeout);

// 输出解析结果
// pframe:传入协议类指针
// ret len:存放解析出来的数据长度
// ret：解析出来的数据指针,NULL代表未解析出数据
extern const uint8_t *b_frame_check_get(b_frame_type *pframe, uint16_t *len);

// 使用PROTOCOL_CORE_EXTERN b_frame_type定义协议类，需要多少就定义多少
PROTOCOL_CORE_EXTERN b_frame_type *spi_frame;

#endif
